import Foundation

public protocol DWJsonConvert {
    
    init()
    
    /// 键值替换，如果要忽略，value传空字符串
    static func dw_replacePropertyMapper() -> [String : String]?
        
}

public extension DWJsonConvert {

    @discardableResult
    /// json转model
    /// - Parameter dic: json字典
    /// - Returns: model
    static func dw_model(_ dic : [String : Any]) -> Self {
        var _model = Self.init()
        let _info = _model.dw_convertInfo()
        if let _point = _info.point {
            var modelInfo : DWModelInfo? = _info.modelInfo
            while let _modelInfo = modelInfo {
                for typeInfo in _modelInfo.propertyList {
                    if typeInfo.ignoreProperty == false {
                        if let jsonValue = dic[typeInfo.replaceKey ?? typeInfo.propertyKey] {
                            if let value = dw_model_convert(jsonValue, info: typeInfo) {
                                _point.advanced(by: typeInfo.offset).dw_set(value, typeInfo.propertyType)
                            }
                        }
                    }
                }
                modelInfo = _modelInfo.superInfo
            }
        }
        return _model
    }
    
    /// mode转json
    /// - Returns: json字典
    func dw_json() -> [String : Any]{
        var _model = self
        let _info = _model.dw_convertInfo()
        var result : [String : Any] = [:]
        if let _point = _info.point {
            var modelInfo : DWModelInfo? = _info.modelInfo
            while let _modelInfo = modelInfo {
                for typeInfo in _modelInfo.propertyList {
                    if let jsonValue = dw_json_convert(_point.advanced(by: typeInfo.offset).dw_get(typeInfo.propertyType), info: typeInfo) {
                        result[typeInfo.propertyKey] = jsonValue
                    }
                }
                modelInfo = _modelInfo.superInfo
            }
        }
        return result
    }
    
    static func dw_replacePropertyMapper() -> [String : String]? {
        return nil
    }
    
    /// 获取所有的属性
    /// - Returns: key：属性名称，value：属性类型
    func dw_propertyList() -> [String : Any] {
        var propertyList : [String : Any] = [:]
        var modelInfo : DWModelInfo?        
        if let displayStyle = Mirror(reflecting: self).displayStyle {
            switch displayStyle {
                case .struct:
                    modelInfo = DWMetaDataManager.dw_reloadModelInfo(Self.self, displayStyle: .dw_struct_style)
                case .class:
                    modelInfo = DWMetaDataManager.dw_reloadModelInfo(Self.self, displayStyle: .dw_class_style)
                default:
                    modelInfo = nil
            }
        }
        while modelInfo != nil {
            for propertyInfo in modelInfo?.propertyList ?? [] {
                propertyList[propertyInfo.propertyKey] = propertyInfo.propertyType
            }
            modelInfo = modelInfo?.superInfo
        }
        return propertyList
    }
}

private extension DWJsonConvert {
    
    mutating func dw_convertInfo() -> (point: UnsafeMutableRawPointer?, modelInfo: DWModelInfo?) {
        if let displayStyle = Mirror(reflecting: self).displayStyle {
            switch displayStyle {
                case .struct:
                    let mutableTypedPointer = withUnsafeMutablePointer(to: &self) {
                        return UnsafeRawPointer($0).bindMemory(to: Int8.self, capacity: MemoryLayout<Self>.stride)
                    }
                    return (UnsafeMutableRawPointer(mutating: mutableTypedPointer), DWMetaDataManager.dw_reloadModelInfo(Self.self, displayStyle: .dw_struct_style))
                case .class:
                    let opaquePointer = Unmanaged.passUnretained(self as AnyObject).toOpaque()
                    let mutableTypedPointer = opaquePointer.bindMemory(to: Int8.self, capacity: MemoryLayout<Self>.stride)
                    return (UnsafeMutableRawPointer(mutableTypedPointer), DWMetaDataManager.dw_reloadModelInfo(Self.self, displayStyle: .dw_class_style))
                default:
                    return (nil, nil)
            }
        }
        return (nil, nil)
    }
    
}
